#pragma once 

#include <memory>
#include <string>
#include <cassert>
#include <iostream>
#include <functional>
#include <unordered_map>

#include "Log.hpp"
#include "Util.hpp"
#include "Epoll.hpp"
#include "Socket.hpp"
#include "Protocol.hpp"

class Connection;
class EpollServer;

static const int gport = 8888;
static const int bsize = 1024;
using func_t = std::function<void(Connection*, const protocol_ns::Request&)>;
using callback_t = std::function<void(Connection*)>;

class Connection {
    public:
        Connection(const int& fd, const std::string& clientip, const uint16_t& clientport) 
            : fd_(fd), clientip_(clientip), clientport_(clientport) {}
        
        void Register(callback_t recver, callback_t sender, callback_t excepter) {
            recver_ = recver;
            sender_ = sender;
            excepter_ = excepter;
        }
        ~Connection() {}
    public:
        int fd_;
        std::string inbuffer_;
        std::string outbuffer_;

        // client info
        std::string clientip_;
        uint16_t clientport_;

        callback_t recver_;
        callback_t sender_;
        callback_t excepter_;

        uint32_t events;

        EpollServer *R_;
};

class EpollServer {
         static const int gnum = 64;
    public:
        EpollServer(func_t func, uint16_t port = gport) : func_(func), port_(port) {}
        void InitServer() {
            listensock_.Socket();
            listensock_.Bind(port_);
            listensock_.Listen();
            epoller_.Create();
            AddConnection(listensock_.Fd(), EPOLLIN | EPOLLET);
            logMessage(DEBUG, "init server success");
        }

        // 事件派发器
        void Disptcher() {
            int timeout = -1;
            while (true) 
                LoopOnce(timeout);
        }

        void AddConnection(int fd, uint32_t events, std::string ip = "127.0.0.1", uint16_t port = gport) {
            // 设置fd 是非阻塞
            if (events & EPOLLET) Util::SetNonBlock(fd);

            // 1 为listensock 创建对应的connection对象
            Connection *conn = new Connection(fd, ip, port);

            if (fd == listensock_.Fd()) {
                conn->Register(std::bind(&EpollServer::GetNewLink, this, std::placeholders::_1), nullptr, nullptr);
            }
            else {
                conn->Register( std::bind(&EpollServer::GetSockMessage, this, std::placeholders::_1), 
                                std::bind(&EpollServer::Sender, this, std::placeholders::_1), 
                                std::bind(&EpollServer::Excepter, this, std::placeholders::_1));
            }
            conn->events = events;
            conn->R_ = this;
            // 2 将connection对象添加到connections_中
            connections_.insert({fd, conn});
            // 3 将listensock_添加到epoll中
            bool r = epoller_.AddEvent(fd, events);
            assert(r);
            (void)r;
            logMessage(DEBUG, "AddConnection success, fd: %d, clientinfo: [%s:%d]", fd, ip.c_str(), port);
        }


        void LoopOnce(int timeout) {
            int n = epoller_.Wait(revs, gnum, timeout);
            for (int i = 0; i < n; i++) {

                int fd = revs[i].data.fd;
                uint32_t events = revs[i].events; 
                logMessage(DEBUG, "当前正在处理%d上的%s", fd, (events & EPOLLIN) ? "EPOLLIN" : "OTHER");
                if ((events & EPOLLERR) || (events & EPOLLHUP)) 
                    events |= (EPOLLIN | EPOLLOUT);

                if ((events & EPOLLIN) && ConnIsExists(fd))  
                    connections_[fd]->recver_(connections_[fd]);
                if ((events & EPOLLOUT) && ConnIsExists(fd)) 
                    connections_[fd]->sender_(connections_[fd]);
                
            }
        }

        // 链接管理器
        void GetNewLink(Connection* conn) {
            do 
            {
                int err = 0;
                std::string clientip;
                uint16_t clientport;
                int sock = listensock_.Accept(&clientip, &clientport, &err);
                if (sock > 0)  {
                    AddConnection(sock, EPOLLIN | EPOLLET, clientip, clientport);
                } else {
                    if (err == EAGAIN || err == EWOULDBLOCK) break;
                    else if (err == EINTR) continue;
                    else {
                        logMessage(WARRING, "errstring :%s errcode: %d", strerror(err), err);
                        continue;
                    }
                }
            } while (conn->events & EPOLLET);
            logMessage(DEBUG, "accepter done ...");
        }

        bool EnableReadWrite(Connection* conn, bool readable, bool writeable) {
            conn->events = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0); 
            return epoller_.ModEvent(conn->fd_, conn->events);
        }

        void GetSockMessage(Connection* conn) {
            do {
                char buffer[bsize];
                ssize_t n = recv(conn->fd_, buffer, sizeof(buffer) - 1, 0);
                if (n > 0) {
                    buffer[n] = 0;
                    conn->inbuffer_ += buffer;

                    std::string request_str;
                    int size = protocol_ns::ParsePackage(conn->inbuffer_, &request_str);
                    if (size > 0) {
                        request_str = protocol_ns::RemoveHeader(request_str, size);
                        protocol_ns::Request req;
                        req.Deserialize(request_str);
                        func_(conn, req);
                    }
                }
                else if (n == 0) {
                    conn->excepter_(conn);
                }
                else {
                    if (errno == EAGAIN || errno == EWOULDBLOCK) { // 读取完毕
                        break;
                    } 
                    else if (errno == EINTR) { // 被信号中断
                        continue;
                    }
                    else { // 真的出错
                        conn->excepter_(conn);
                        break;
                    }
                }
            } while (conn->events & EPOLLET);
        }

        void Sender(Connection* conn) {
            do {
                ssize_t n = send(conn->fd_, conn->outbuffer_.c_str(), conn->outbuffer_.size(), 0);
                if (n > 0) {
                    conn->outbuffer_.erase(0, n);
                    if (conn->outbuffer_.empty()) {
                        EnableReadWrite(conn, true, false);
                        break;
                    }
                    else 
                        EnableReadWrite(conn, true, true);
                }
                else {
                    if (errno == EAGAIN || errno == EWOULDBLOCK) break;
                    else if (errno == EINTR) continue;
                    else {
                        conn->excepter_(conn);
                        break;
                    }
                }
            } while (conn->events & EPOLLET);
        }

        void Excepter(Connection* conn) {
            logMessage(DEBUG, "Excepter..., fd : %d, clientinfo: [%s:%d]", conn->fd_, conn->clientip_.c_str(), conn->clientport_);
        }

        bool ConnIsExists(int fd) {
            return connections_.find(fd) != connections_.end();
        }


        ~EpollServer() {
            listensock_.Close();
            epoller_.Close();
        }

    private:
        uint16_t port_;
        Sock listensock_;
        Epoller epoller_;
        struct epoll_event revs[gnum];
        func_t func_;
        std::unordered_map<int, Connection*> connections_;
};